package com.dotflat.fin.service.impl;

import com.alibaba.fastjson.JSON;
import com.dotflat.fin.cnst.APPConstants;
import com.dotflat.fin.domain.*;
import com.dotflat.fin.dto.AppPayResult;
import com.dotflat.fin.model.APPPayAttach;
import com.dotflat.fin.repository.*;
import com.dotflat.fin.service.*;
import com.dotflat.fin.utils.WxToolUtil;
import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.github.binarywang.wxpay.util.SignUtils;
import com.google.common.collect.Maps;
import com.matt.commons.exception.BusinessException;

import com.matt.foundation.model.ErrorCode;
import com.matt.domain.Clientele;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.*;

import static com.dotflat.fin.cnst.Const.PHOTO_ALBUM_PRINT_PRICE;


/**
 * 微信支付
 * @author ：Ron
 * @date ：Created in 2020/3/17 20:26
 */
@Service
@Slf4j
public class WechatPayServiceImpl implements WechatPayService {

    @Autowired
    private PayRecordRepository payRecordRepository;

    @Autowired
    private PayServiceProxy payServiceProxy;

    @Autowired
    private WxPayService wxMpPayService;

    @Autowired
    private WxPayService wxMaPayService;

    @Autowired
    private Map<String, WxPayService> areaPayServices;

    @Autowired
    private WxPayConfig wxMpPayConfig;

    @Autowired
    private FinPayAccountService accountService;

    @Autowired
    private FinPlanOrderRepository planOrderRepository;

    @Autowired
    private FinPayAccountRepository payAccountRepository;

    @Autowired
    private WalletService walletService;

    private WxPayService areaPayService(String areaId, String preferPayAccountIds, boolean mpOrNot){

        log.info("请求微信支付服务");
        log.info("areaId: {}", areaId);
        log.info("preferPayAccountIds: {}", preferPayAccountIds);
        log.info("mpOrNot: {}", mpOrNot);

        FinPayAccount areaAccount = null;
        if (StringUtils.isNotBlank(preferPayAccountIds)){

            String[] accountIds = preferPayAccountIds.split("\\|");

            Set<String> ids = new HashSet<>(Arrays.asList(accountIds));

            List<FinPayAccount> accs = payAccountRepository.findAllByAreaIdAndIdInAndDeletedIsFalse(areaId, ids);

            for (FinPayAccount acc: accs){
                if (acc.getAccountType() == (mpOrNot?3:1)){
                    areaAccount = acc;

                    log.info("找到指定账号 {}", acc);

                    break;
                }
            }
        }

        if (areaAccount == null){
            areaAccount = accountService.activedPayAccount(areaId, mpOrNot?3:1);
            log.info("找到激活账号 {}", areaAccount);
        }

        if (areaAccount != null){
            //获取service
            WxPayService areaPayService = areaPayServices.get(areaId);
            if (areaPayService == null){
                //创建新的payConfig
                WxPayConfig payConfig = new WxPayConfig();
                payConfig.setAppId(areaAccount.getAppId());
                payConfig.setMchId(areaAccount.getMchId());
                payConfig.setMchKey(areaAccount.getMchKey());
                payConfig.setNotifyUrl(wxMpPayConfig.getNotifyUrl());
                payConfig.setKeyPath("");
                payConfig.setTradeType("JSAPI");
                payConfig.setSignType("MD5");

                areaPayService = new WxPayServiceImpl();
                areaPayService.setConfig(payConfig);

                log.info("找到园区账号 {}", areaAccount);

                areaPayServices.put(areaId, areaPayService);
            }
            return areaPayService;
        }

        log.info("找到默认账号 {}", areaAccount);

        return mpOrNot?wxMpPayService:wxMaPayService;
    }

    private WxPayUnifiedOrderRequest buildPayUnifiedOrderRequest(String openId, String outTradeNo, String title, Integer totalAmount,
                                         APPPayAttach attach, String notify_url){
        WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = null;
        if (StringUtils.isNotEmpty(openId)){
            wxPayUnifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
                    .openid(openId)
                    .outTradeNo(outTradeNo)
                    .totalFee(totalAmount)
                    .body("支付帐单款项：" + title)
                    .tradeType(APPConstants.TradeTypeWX.JSAPI.getName())
                    .spbillCreateIp(WxToolUtil.getIpAddress())
                    .attach(JSON.toJSONString(attach))
                    .notifyUrl(notify_url)
                    .build();
        } else {
            wxPayUnifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
                    .outTradeNo(outTradeNo)
                    .totalFee(totalAmount)
                    .body("支付帐单款项：" + title)
                    .tradeType(APPConstants.TradeTypeWX.APP.getName())
                    .spbillCreateIp(WxToolUtil.getIpAddress())
                    .attach(JSON.toJSONString(attach))
                    .notifyUrl(notify_url)
                    .build();
        }
        return wxPayUnifiedOrderRequest;
    }

    @Override
    public Map<String,Object> prepare(Clientele clientele,
                                      String openId,
                                      Integer resourceType, String resourceId,
                                      long useDeposit){



        //交易相关信息
        if (useDeposit > 0) {
            //TODO： 检查是否有足够的定金
            Wallet wallet = walletService.queryWalletByClientele(clientele);
            if (wallet == null || wallet.getBalance() < useDeposit) {
                throw new BusinessException(ErrorCode.PRODUCT_NOT_EXIST, "余额不足");
            }
        }


        //创建本地订单
        PayRecord payRecord = new PayRecord();
//        payRecord.setPayOrderId(finPlanOrder.getId());
        payRecord.setCreatedBy(clientele.getId());
        payRecord.setAreaId(clientele.getAreaId());
        payRecord.setUseDeposit(useDeposit);

        payRecordRepository.save(payRecord);

        String outTradeNo = payRecord.getId();

        Object wxPayAppOrderResult = null;
        try {
            //获取园区的支付服务
            WxPayService wxPayService = areaPayService(clientele.getAreaId(), null, StringUtils.isNotBlank(openId));

            //创建请求

            //获取相册的价格
            String title = "客户自定义服务购买";

            int amount = PHOTO_ALBUM_PRINT_PRICE - (int)useDeposit;  //减掉从定金里面扣的钱

            APPPayAttach attach = new APPPayAttach(resourceType,  resourceId);

            WxPayUnifiedOrderRequest uOrderRequest = buildPayUnifiedOrderRequest(openId, outTradeNo,
                    title,
                    amount,
                    attach, wxPayService.getConfig().getNotifyUrl());

            wxPayAppOrderResult = wxPayService.createOrder(uOrderRequest);

            payRecord.setCreatedBy(clientele.getId());
            payRecord.setPayId(uOrderRequest.getAppid());
            payRecord.setAttach(uOrderRequest.getAttach());

            payRecord.setTradeType(uOrderRequest.getTradeType());
            payRecord.setTotalFee(uOrderRequest.getTotalFee() + 0L);  //总价

            payRecord.setCreated(DateTime.now().toDate());
            payRecord.setPayStatus(APPConstants.PayOrderStatus.NON_PAYMENT.getValue());

            payRecordRepository.save(payRecord);
        } catch (WxPayException e) {
            e.printStackTrace();
            String errorMsg = e.getReturnMsg();
            throw new BusinessException(ErrorCode.VERSION_ERROR, errorMsg);
        }


        Map<String,Object> result = Maps.newHashMap();

        result.put("wxPayResult",wxPayAppOrderResult);
        result.put("outTradeNo",outTradeNo);

        return result;
    }


    @Override
    public Map<String,Object> prepare(Clientele clientele,
                                      String openId,
                                      Integer resourceType,
                                      FinPlanOrder finPlanOrder,
                                      long useDeposit){



        //交易相关信息
        if (useDeposit > 0) {
            //TODO： 检查是否有足够的定金
            Wallet wallet = walletService.queryWalletByClientele(clientele);
            if (wallet == null || wallet.getBalance() < useDeposit) {
                throw new BusinessException(ErrorCode.PRODUCT_NOT_EXIST, "余额不足");
            }
        }


        //创建本地订单
        PayRecord payRecord = new PayRecord();
        payRecord.setPayOrderId(finPlanOrder.getId());
        payRecord.setCreatedBy(clientele.getId());
        payRecord.setAreaId(clientele.getAreaId());
        payRecord.setUseDeposit(useDeposit);

        payRecordRepository.save(payRecord);

        String outTradeNo = payRecord.getId();

        Object wxPayAppOrderResult = null;
        try {
            //获取园区的支付服务
            WxPayService wxPayService = areaPayService(finPlanOrder.getAreaId(), finPlanOrder.getPayAccountIds(), StringUtils.isNotBlank(openId));

            //创建请求
            int amount = finPlanOrder.getTotalAmount() - (int)useDeposit;  //减掉从定金里面扣的钱
            APPPayAttach attach = new APPPayAttach(resourceType,  finPlanOrder.getId());
            WxPayUnifiedOrderRequest uOrderRequest = buildPayUnifiedOrderRequest(openId, outTradeNo,
                    finPlanOrder.getTitle(),
                    amount,
                    attach, wxPayService.getConfig().getNotifyUrl());

            wxPayAppOrderResult = wxPayService.createOrder(uOrderRequest);

            payRecord.setCreatedBy(clientele.getId());
            payRecord.setPayId(uOrderRequest.getAppid());
            payRecord.setAttach(uOrderRequest.getAttach());

            payRecord.setTradeType(uOrderRequest.getTradeType());
            payRecord.setTotalFee(uOrderRequest.getTotalFee() + 0L);  //总价

            payRecord.setCreated(DateTime.now().toDate());
            payRecord.setPayStatus(APPConstants.PayOrderStatus.NON_PAYMENT.getValue());

            payRecordRepository.save(payRecord);
        } catch (WxPayException e) {
            e.printStackTrace();
            String errorMsg = e.getReturnMsg();
            throw new BusinessException(ErrorCode.VERSION_ERROR, errorMsg);
        }


        Map<String,Object> result = Maps.newHashMap();

        result.put("wxPayResult",wxPayAppOrderResult);
        result.put("outTradeNo",outTradeNo);

        return result;
    }

    @Override
    @Transactional(rollbackOn = Exception.class)
    public String processWXOrderResult(Map<String, String> notifyMap, WxPayOrderQueryResult payOrderQueryResult) {

        if (notifyMap == null){
            notifyMap = new HashMap<>();
        }

        String attach = notifyMap.get("attach");
        String resultCode = notifyMap.get("result_code");
        String transactionId = notifyMap.get("transaction_id");
        String outTradeNo = notifyMap.get("out_trade_no");
        String totalFee = notifyMap.get("total_fee");

        if (payOrderQueryResult != null){
            attach = payOrderQueryResult.getAttach();

            resultCode = payOrderQueryResult.getResultCode();

            transactionId = payOrderQueryResult.getTransactionId();

            outTradeNo = payOrderQueryResult.getOutTradeNo();

            Object o = payOrderQueryResult.getTotalFee();
            totalFee = o.toString();
        }

        if (!"SUCCESS".equals(resultCode)){
            return null;
        }

        APPPayAttach p = JSON.parseObject(attach, APPPayAttach.class);

        long tf = 0;
        if (StringUtils.isNotEmpty(totalFee)){
            tf = Long.parseLong(totalFee);
        }

        AppPayResult payResult = null;
        if (payOrderQueryResult != null){
            payResult = AppPayResult.of(payOrderQueryResult);
        }else{
            payResult = AppPayResult.of(notifyMap);
        }

        return payServiceProxy.processContractPayment("wechat",p, transactionId, outTradeNo, tf, payResult);

    }

    @Override
    public String wxOrderCheck(String outTradeNo){

        WxPayOrderQueryRequest wxPayOrderQueryRequest = WxPayOrderQueryRequest.newBuilder()
                .outTradeNo(outTradeNo)
                .build();

        //get pay
        PayRecord payRecord = payRecordRepository.getOne(outTradeNo);
        if (payRecord == null){
            return null;
        }

        FinPlanOrder planOrder = planOrderRepository.getOne(payRecord.getPayOrderId());

        if (planOrder == null){
            return null;
        }

        try {

            WxPayService payService = areaPayService(planOrder.getAreaId(), planOrder.getPayAccountIds(), true);

            WxPayOrderQueryResult wxPayOrderQueryResult = payService.queryOrder(wxPayOrderQueryRequest);
            String tradeState = wxPayOrderQueryResult.getTradeState();
            if (tradeState.equals("SUCCESS")) {
                //TODO, finish task
                String ret = processWXOrderResult(null, wxPayOrderQueryResult);
                return ret;
            }
        } catch (WxPayException e) {
            e.printStackTrace();
        }

        return null;
    }

    @Override
    public boolean wxCheckSign(Map<String, String> notifyMap){

        String appId = notifyMap.get("appid");
        String outTradeNo = notifyMap.get("out_trade_no");
        PayRecord payRecord = payRecordRepository.getOne(outTradeNo);

        FinPlanOrder planOrder = null;
        if (StringUtils.isNotEmpty(payRecord.getPayOrderId())) {
            planOrder = planOrderRepository.getOne(payRecord.getPayOrderId());
        }

        boolean mpOrNot = wxMpPayConfig.getAppId().equals(appId);
        WxPayService wxPayService = areaPayService(payRecord.getAreaId(), planOrder == null ? null : planOrder.getPayAccountIds(), mpOrNot);

        WxPayConfig config = wxPayService.getConfig();

        return SignUtils.checkSign(notifyMap, config.getSignType(), config.getMchKey());
    }

}
